#pragma once

#include <mutex>
#include <string>
#include <signal.h>
#include "util.hh"
#include "conf.hh"
#include "info.hh"
#include "persist.hh"

namespace cloud
{

class data_manager
{
public:
    static data_manager& instance()
    {
        static data_manager inst;
        return inst;
    }

    backup_info* update(const backup_info& info)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return &(_bpath2info[info.bpath] = info);
    }

    backup_info* insert(const backup_info& info)
    {
        return update(info);
    }

    backup_info* get_by_bpath(const std::string& bpath)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        // TODO: 死锁！这是死锁的哪个条件？
        // if (_fpath2info.find(fpath) == _fpath2info.end())
        //     return handle_error(fpath);

        if (_bpath2info.find(bpath) == _bpath2info.end())
            return nullptr;

        return &_bpath2info[bpath];
    }

    std::vector<backup_info> get_all()
    {
        std::unique_lock<std::mutex> lock(_mtx);

        std::vector<backup_info> v;
        for (auto& kv : _bpath2info)
            v.push_back(kv.second);

        return v;
    }

    bool delete_by_bpath(const std::string& bpath)
    {
        std::unique_lock<std::mutex> lock(_mtx);
        return _bpath2info.erase(bpath);
    }

private:
    data_manager()
        : _persister(persist_factory::create<file_persist>())
    {
        util::file record(config::instance().record_file());

        if (!record.exists())
            record.create();

        load();

        for (int i = 1; i < 32; i++)
            signal(i, signal_handler);

        easylog::info("data manager created");
    }

    ~data_manager() { store(); }

    static void signal_handler(int sig)
    {
        easylog::info("received signal {}, persist now", sig);
        data_manager::instance().store();
        util::killer::kill_self();
    }

    void load()
    {
        std::vector<backup_info> infos = _persister->load();
        for (auto& info : infos)
            insert(info);
    }

    void store()
    {
        _persister->store(get_all());
    }

    backup_info* handle_error(const std::string& bpath)
    {
        if (util::path::exists(bpath))
        {
            return insert(backup_info(bpath));
        }
        else
        {
            easylog::info("the file is not exists, check {}", bpath);
            return nullptr;
        }
    }

private:
    std::unordered_map<std::string, backup_info> _bpath2info;
    std::mutex _mtx;
    persist::ptr _persister;
};

} // namespace cloud
